package cn.tnar.yunpark.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

public class RequestDumpFilter implements Filter {


    private static final Logger logger = LoggerFactory.getLogger(RequestDumpFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        try {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;

            Map<String, String> requestMap = this.getTypesafeRequestMap(httpServletRequest);
            BufferedRequestWrapper bufferedReqest = new BufferedRequestWrapper(httpServletRequest);

            final StringBuilder logMessage = new StringBuilder()
                    .append("[")
                    .append(httpServletRequest.getMethod())
                    .append(" uri=")
                    .append(httpServletRequest.getRequestURI())
                    .append(";client=")
                    .append(httpServletRequest.getRemoteAddr())
                    .append(";params=")
                    .append(requestMap)
                    .append(";contentType=")
                    .append(bufferedReqest.getContentType())
                    .append(";body=")
                    .append(bufferedReqest.getRequestBody())
                    .append("]");
            String s = logMessage.toString();
            if (s.contains("REPORT_HEART_BEAT")) {
                logger.debug(s);
            } else {
                logger.info(s);
            }

            chain.doFilter(bufferedReqest, httpServletResponse);
        } catch (Throwable a) {
            logger.error(a.getMessage());
        }
    }


    private Map<String, String> getTypesafeRequestMap(HttpServletRequest request) {
        Map<String, String> typesafeRequestMap = new HashMap<String, String>();
        Enumeration<?> requestParamNames = request.getParameterNames();
        while (requestParamNames.hasMoreElements()) {
            String requestParamName = (String) requestParamNames.nextElement();
            String requestParamValue = request.getParameter(requestParamName);
            typesafeRequestMap.put(requestParamName, requestParamValue);
        }
        return typesafeRequestMap;
    }


    @Override
    public void destroy() {
    }


    private static final class BufferedRequestWrapper extends HttpServletRequestWrapper {

        private ByteArrayInputStream bais = null;
        private ByteArrayOutputStream baos = null;
        private BufferedServletInputStream bsis = null;
        private byte[] buffer = null;


        public BufferedRequestWrapper(HttpServletRequest req) throws IOException {
            super(req);
            // Read InputStream and store its content in a buffer.
            InputStream is = req.getInputStream();
            this.baos = new ByteArrayOutputStream();
            byte buf[] = new byte[1024];
            int letti;
            while ((letti = is.read(buf)) > 0) {
                this.baos.write(buf, 0, letti);
            }
            this.buffer = this.baos.toByteArray();
        }


        @Override
        public ServletInputStream getInputStream() {
            this.bais = new ByteArrayInputStream(this.buffer);
            this.bsis = new BufferedServletInputStream(this.bais);
            return this.bsis;
        }


        String getRequestBody() throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(this.getInputStream()));
            String line = null;
            StringBuilder inputBuffer = new StringBuilder();
            do {
                line = reader.readLine();
                if (null != line) {
                    inputBuffer.append(line.trim());
                }
            } while (line != null);
            reader.close();
            return inputBuffer.toString().trim();
        }

    }


    private static final class BufferedServletInputStream extends ServletInputStream {

        private ByteArrayInputStream bais;

        public BufferedServletInputStream(ByteArrayInputStream bais) {
            this.bais = bais;
        }

        @Override
        public int available() {
            return this.bais.available();
        }

        @Override
        public int read() {
            return this.bais.read();
        }

        @Override
        public int read(byte[] buf, int off, int len) {
            return this.bais.read(buf, off, len);
        }


        @Override
        public boolean isFinished() {
            return this.bais.available() <= 0;
        }

        @Override
        public boolean isReady() {
            return true;
        }

        @Override
        public void setReadListener(ReadListener listener) {

        }
    }

//    public class TeeServletOutputStream extends ServletOutputStream {
//
//    	private final TeeOutputStream targetStream;
//
//    	public TeeServletOutputStream( OutputStream one, OutputStream two ) {
//    		targetStream = new TeeOutputStream( one, two);
//    	}
//
//		@Override
//		public void write(int arg0) throws IOException {
//			this.targetStream.write(arg0);
//		}
//
//		public void flush() throws IOException {
//			super.flush();
//			this.targetStream.flush();
//		}
//
//		public void close() throws IOException {
//			super.close();
//			this.targetStream.close();
//		}
//    }

}